﻿using System;
using System.IO;
using System.Net;
using System.Threading;

namespace Conusic
{
    public sealed class DownloadFile
    {
        #region Delegates

        public delegate void FileEventHandler(FileEventArgs args);

        #endregion

        private const int BufferSize = 1024*16;

        private readonly FileEventArgs fileEventArgs;
        private readonly string saveToFile;
        private readonly string url;
        public byte[] Buffer;
        public int ReadedBytes;

        public DownloadFile(string url, string saveToFile)
        {
            fileEventArgs = new FileEventArgs(this);
            this.url = url;
            this.saveToFile = saveToFile;
        }

        public event FileEventHandler OnFileComplete;
        public event FileEventHandler OnFileFail;
        public event FileEventHandler OnProgressFile;

        public void DownloadAsync()
        {
            ThreadStart threadStart = Download;
            var thread = new Thread(threadStart);
            thread.Start();
        }

        public void Download()
        {
            // Fixed download count execution. Stop, until semaphore is busy.
            Config.Instance.DownloadPool.WaitOne();

            fileEventArgs.Url = url;
            fileEventArgs.SaveToFile = saveToFile;
            // Declare object for safe download and release semaphore
            FileStream outputTempFile = null;
            FileStream outputLockFile = null;
            HttpWebRequest request = null;
            Stream datastream = null;

            // We want to save only complete file
            // so save download part in temp file
            string tempFileName = Path.GetTempFileName();
            try
            {
                // Lock and create file for download
                outputLockFile = new FileStream(saveToFile, FileMode.CreateNew);

                outputTempFile = new FileStream(tempFileName, FileMode.Create);
                request = (HttpWebRequest) WebRequest.Create(url);

                // Set proxy configuration for request
                Config.Instance.SetProxyConfiguration(request);

                WebResponse response = request.GetResponse();
                datastream = response.GetResponseStream();

                if (datastream == null)
                    throw new WebException("Response stream is null");

                ReadedBytes = 0;
                fileEventArgs.Total = response.ContentLength;


                long totalReaded = 0;
                Buffer = new byte[BufferSize];
                do
                {
                    ReadedBytes = datastream.Read(Buffer, 0, BufferSize);

                    outputTempFile.Write(Buffer, 0, ReadedBytes);
                    totalReaded += ReadedBytes;

                    fileEventArgs.Downloaded = totalReaded;
                    if (OnProgressFile != null)
                        OnProgressFile(fileEventArgs);
                } while (totalReaded < response.ContentLength);
                outputTempFile.Close();

                // Unlock file and copy
                outputLockFile.Close();
                File.Copy(tempFileName, saveToFile,true);

                if (OnFileComplete != null)
                    OnFileComplete(fileEventArgs);
            }
            catch (Exception e)
            {
                fileEventArgs.Exc = e;
                SafeDelete(saveToFile);
                
                if (OnFileFail != null)
                    OnFileFail(fileEventArgs);
                else throw;
            }
            finally
            {
                SafeDelete(tempFileName);
                if (outputTempFile != null)
                    outputTempFile.Close();
                if (datastream != null)
                    datastream.Close();
                if (outputLockFile != null)
                    outputLockFile.Close();
            }

            Config.Instance.DownloadPool.Release();
        }

        private void SafeDelete(string filename)
        {
            try
            {
                File.Delete(filename);
            }
            catch
            {
            }
        }
    }
}